"""
Copyright (c) 2012-2013 RockStor, Inc. <http://rockstor.com>
This file is part of RockStor.

RockStor is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

RockStor is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""


from rest_framework.response import Response
from rest_framework.exceptions import NotFound
from django.db import transaction
from storageadmin.models import (Share, Appliance, EmailClient)
from smart_manager.models import (Replica, ReplicaTrail)
from smart_manager.serializers import ReplicaSerializer
from storageadmin.util import handle_exception
from datetime import datetime
from django.utils.timezone import utc
from django.conf import settings
import rest_framework_custom as rfc
import logging
logger = logging.getLogger(__name__)


class ReplicaMixin(object):
    serializer_class = ReplicaSerializer

    @staticmethod
    def _refresh_crontab():
        mail_from = None
        if (EmailClient.objects.filter().exists()):
            eco = EmailClient.objects.filter().order_by('-id')[0]
            mail_from = eco.sender
        with open('/etc/cron.d/replicationtab', 'w') as cfo:
            cfo.write('SHELL=/bin/bash\n')
            cfo.write("PATH=/sbin:/bin:/usr/sbin:/usr/bin\n")
            cfo.write("MAILTO=root\n")
            if (mail_from is not None):
                cfo.write("MAILFROM=%s\n" % mail_from)
            cfo.write("# These entries are auto generated by Rockstor."
                      " Do not edit.\n")
            for replica in Replica.objects.filter(enabled=True):
                if (replica.crontab is not None):
                    cfo.write('%s root %sbin/send-replica %d\n' %
                              (replica.crontab, settings.ROOT_DIR, replica.id))

    @staticmethod
    def _validate_port(port, request):
        try:
            port = int(port)
        except ValueError:
            e_msg = ('Remote Listener port must be a valid port '
                     'number(1-65535)')
            handle_exception(Exception(e_msg), request)

        if (port < 1 or port > 65535):
            e_msg = ('Valid port numbers are between 1-65535')
            handle_exception(Exception(e_msg), request)
        return port


class ReplicaListView(ReplicaMixin, rfc.GenericView):

    def get_queryset(self, *args, **kwargs):
        status = self.request.query_params.get('status', None)
        if (status is not None):
            enabled = None
            if (status == 'enabled'):
                enabled = True
            elif (status == 'disabled'):
                enabled = False
            if (enabled is not None):
                return Replica.objects.filter(enabled=enabled)
        return Replica.objects.filter().order_by('-id')

    @transaction.atomic
    def post(self, request):
        with self._handle_exception(request):
            sname = request.data.get('share')
            if (Replica.objects.filter(share=sname).exists()):
                e_msg = ('Another replication task already exists for this '
                         'share(%s). Only 1-1 replication is supported '
                         'currently.' % sname)
                handle_exception(Exception(e_msg), request)
            share = self._validate_share(sname, request)
            appliance = self._validate_appliance(request)
            dpool = request.data.get('pool')
            crontab = request.data.get('crontab')
            task_name = request.data.get('task_name')
            data_port = self._validate_port(request.data.get('listener_port'),
                                            request)
            replication_ip = request.data.get('listener_ip', None)
            if (replication_ip is not None and
                    len(replication_ip.strip()) == 0):
                replication_ip = None
            ts = datetime.utcnow().replace(tzinfo=utc)
            r = Replica(task_name=task_name, share=sname,
                        appliance=appliance.uuid, pool=share.pool.name,
                        dpool=dpool, enabled=True, crontab=crontab,
                        data_port=data_port, ts=ts,
                        replication_ip=replication_ip)
            r.save()
            self._refresh_crontab()
            return Response(ReplicaSerializer(r).data)

    @staticmethod
    def _validate_share(sname, request):
        try:
            return Share.objects.get(name=sname)
        except:
            e_msg = ('Share: %s does not exist' % sname)
            handle_exception(Exception(e_msg), request)

    @staticmethod
    def _validate_appliance(request):
        try:
            ip = request.data.get('appliance', None)
            return Appliance.objects.get(ip=ip)
        except:
            e_msg = ('Appliance with ip(%s) is not recognized.' % ip)
            handle_exception(Exception(e_msg), request)


class ReplicaDetailView(ReplicaMixin, rfc.GenericView):

    def get(self, *args, **kwargs):
        try:
            data = Replica.objects.get(id=self.kwargs['rid'])
            serialized_data = ReplicaSerializer(data)
            return Response(serialized_data.data)
        except Replica.DoesNotExist:
            raise NotFound(detail=None)

    @transaction.atomic
    def put(self, request, rid):
        with self._handle_exception(request):
            try:
                r = Replica.objects.get(id=rid)
            except:
                e_msg = ('Replica(%s) does not exist' % rid)
                handle_exception(Exception(e_msg), request)

            r.crontab = request.data.get('crontab', r.crontab)
            enabled = request.data.get('enabled', r.enabled)
            if (type(enabled) != bool):
                e_msg = ('enabled switch must be a boolean, not %s' %
                         type(enabled))
                handle_exception(Exception(e_msg), request)
            r.enabled = enabled
            replication_ip = request.data.get('listener_ip', r.replication_ip)
            if (replication_ip is not None and
                    len(replication_ip.strip()) == 0):
                replication_ip = None
            r.replication_ip = replication_ip
            r.data_port = self._validate_port(
                request.data.get('listener_port', r.data_port), request)
            ts = datetime.utcnow().replace(tzinfo=utc)
            r.ts = ts
            r.save()
            self._refresh_crontab()
            return Response(ReplicaSerializer(r).data)

    def delete(self, request, rid):
        with self._handle_exception(request):
            try:
                r = Replica.objects.get(id=rid)
            except:
                e_msg = ('Replica(%s) does not exist' % rid)
                handle_exception(Exception(e_msg), request)

            if (r.enabled is True):
                e_msg = ('Replica(%s) is enabled. If you are sure, disable it '
                         'first and then delete.' % rid)
                handle_exception(Exception(e_msg), request)

            ReplicaTrail.objects.filter(replica=r).delete()
            r.delete()
            self._refresh_crontab()
            return Response()
